/*
 * UDP.java
 * 
 * last update: 16.01.2010 by Stefan Saru
 * 
 * author:	Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */

package engine;

import java.util.*;
import middle.ScopeNode;

/**
 * An UDP instance (both combinational and sequential).
 */

class UDP extends Module implements Monitor{

	SelectionExpression inputs,  output;

	BitVector lastValue, lastOutput;

	Delay3 delays;

	UDPTableEntry entries[];

	/**
	 * caches entries.length
	 */
	int length;

	/**
	 * The time at which the update should take place
	 */
	long updateTime;

	UDP (UDPDescription desc, NameSpace parent,
			ModuleInstanceDescription id,
			ScopeNode thisScope) throws ParseException {
		super(desc, parent, id, thisScope);
		downwardAllowed = false;
		desc.instantiateAll(this, thisScope);
		try {
			setupMonitor((SelectionExpression)
					desc.inputExpression.instantiate(this),
					(SelectionExpression)
					desc.outputExpression.instantiate(this),
					desc.delay);
		} catch (InterpretTimeException ex) {
			throw new ParseException(parent.desc.toString(id.lineNo) +
					": error: " +
					ex.toString());
		}
		entries = desc.entries;
		length = entries.length;
	}

	void setupMonitor(SelectionExpression inputs,
			SelectionExpression output,
			Delay3 delays)
	throws InterpretTimeException, ParseException
	{
		this.inputs = inputs;
		this.output = output;
		this.delays = delays;
		xConsole.debug("UDP's delays: " + delays);
		inputs.addMonitor(this);

		if (((UDPDescription)desc).type == UDPDescription.SEQType) {
			BitVector state = ((Register)scopeData.get(((RegisterDescription)
					((UDPDescription) desc).
					regsVector.elementAt(0)).
					name)).getData(0);
			lastOutput = state.getBits();
			state.removeMonitor(this); //we don't want to get notified about state changes !
		} else lastOutput = BitVector.bX();

		lastValue = inputs.evaluate().getBits();;
	}

	public void trigger() throws InterpretTimeException {
		BitVector curValue = inputs.evaluate().getBits();
		if (!curValue.equals(lastValue)) {
			int lastMatchedEntry = -1;
			int lastMatchedEntryType = -1;
			for (int i = 0; i < length ; i++)
				if (entries[i].match(lastValue, curValue)) {
					xConsole.debug("UDP.trigger : matched " + lastValue +
							" - -> " + curValue +
							" as : " + entries[i]);
					int type = entries[i].isEdgeEntry() ? 1 : 0;
					if (type == lastMatchedEntryType)
						xConsole.warn(desc.toString(((UDPDescription) desc).line) +
								": warning: input value: " + curValue +
								" matches at least two entries with the same type: " +
								entries[lastMatchedEntry] + " and " + entries[i]);
					lastMatchedEntryType = type;
					lastMatchedEntry = i;
				}

			BitVector curOutput;

			if (lastMatchedEntry != -1) {
				xConsole.debug("finally matched: " + entries[lastMatchedEntry]);
				if ((curOutput = entries[lastMatchedEntry].output) == null)
					curOutput = lastOutput;
				else
					curOutput = entries[lastMatchedEntry].output;
			} else
				curOutput = BitVector.bX();

			if ((curOutput != lastOutput) && !curOutput.equals(lastOutput)) {
				Time.addFinisher(Wire.computeTransitionDelayFor(lastOutput, curOutput, delays),
						new SimpleAssignInstruction(this,
								output,
								curOutput,
								": " + output + " <<< (" +
										curOutput + ")"));
				lastOutput = curOutput;
			} else
				xConsole.debug("no change");

			lastValue = curValue;
		}
	}

}
